import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;

import networkTraining.DataSet;
import networkTraining.NetworkTrainer;
import networkTraining.algorithms.BackPropagationAlgorithm;
import networkTraining.inspection.NeuronWeightInspector;
import neuralModel.activationFunction.SigmoidActivationFunction;
import neuralNetwork.INeuralNetwork;
import neuralNetwork.NeuralNetworkUtils;
import neuralNetwork.execution.NeuralNetworkExecutor;

public class FlatWorld
{

	private static String NetworksFile = "Networks.xml";

	private NeuronWeightInspector weightInspector;
	private StringBuilder errorRecord;

	static
	{
		System.loadLibrary("libFlatWorld");
	}

	public native void run();

	private StringBuilder deathTime;
	private StringBuilder objectsEaten;
	private Map<String, INeuralNetwork> networkMap;
	private INeuralNetwork visionNet;

	public FlatWorld()
	{
		deathTime = new StringBuilder();
		objectsEaten = new StringBuilder();
		networkMap = new HashMap<String, INeuralNetwork>();
		errorRecord = new StringBuilder("Errors:\n");

	}

	// ///////////////////////NN Methods////////////////////////////
	public int findGreen(double[][] inputs)
	{
		return 0;
	}

	public int findBlue(double[][] inputs)
	{
		return 0;
	}

	public int findRed(double[][] inputs)
	{
		return 0;
	}

	public void trainEye(double rVal, double gVal, double bVal, int energyDelt)
	{
		double sum = rVal + gVal + bVal;
		if (sum == 0)
			sum = 1;

		DataSet dataSet = new DataSet(new String[]
		{ "red", "green", "blue" }, new double[]
		{ rVal / sum, gVal / sum, bVal / sum }, new String[]
		{ "output" }, new double[]
		{ energyDelt });

		Map<String, Double> tmpMap = NetworkTrainer.TrainNetwork(visionNet, dataSet, new BackPropagationAlgorithm(
		    visionNet, 0.5, 0.01));

		errorRecord.append(tmpMap.get("output") + "\n");
		weightInspector.takeSnapShot();

	}

	public double useEye(double rVal, double gVal, double bVal)
	{
		double sum = rVal + gVal + bVal;
		if (sum == 0)
			sum = 1;
		
		DataSet dataSet = new DataSet(new String[]
		{ "red", "green", "blue" }, new double[]
		{ rVal / sum, gVal / sum, bVal / sum }, new String[0], new double[0]);
		

		NeuralNetworkExecutor.ExecuteNetwork(visionNet, dataSet);
		double output = visionNet.getNeuron("output").getOutputValue();
		if(output > .5 )
			return sum;
		else
			return 0;
		//return visionNet.getNeuron("output").getOutputValue() *sum;
	}

	// ///////////////////////Utility Methods///////////////////////
	public void loadNN()
	{
		XMLDecoder decoder = null;
		InputStream stream = null;
		try
		{
			stream = new BufferedInputStream(new FileInputStream(NetworksFile));
			decoder = new XMLDecoder(stream);
			visionNet = (INeuralNetwork) decoder.readObject();
		}
		catch (FileNotFoundException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		finally
		{
			if (decoder != null)
				decoder.close();
		}
	}

	public void saveNN()
	{
		XMLEncoder encoder = null;
		OutputStream stream;
		try
		{
			stream = new BufferedOutputStream(new FileOutputStream(NetworksFile));
			encoder = new XMLEncoder(stream);

			encoder.writeObject(visionNet);
			encoder.flush();

		}
		catch (FileNotFoundException e)
		{
			e.printStackTrace();
		}
		finally
		{
			if (encoder != null)
				encoder.close();
		}

	}

	public void recordDeathTime(int time)
	{
		deathTime.append(time + "\n");
	}

	public void recordObjectEaten(int count)
	{
		objectsEaten.append(count + "\n");
	}

	public void recordFoodEaten(int count)
	{

	}

	public void recordPoisenEaten(int count)
	{

	}

	public static void main(String[] args) throws InterruptedException
	{

		FlatWorld flatWorld = new FlatWorld();

	//	flatWorld.visionNet = NeuralNetworkUtils.buildFeedForwardNetwork(new String[]
	//	{ "red", "green", "blue" }, new String[]
	//	{ "output" }, new int[0], new SigmoidActivationFunction(), 0.5);

		 flatWorld.loadNN();

		flatWorld.weightInspector = new NeuronWeightInspector(flatWorld.visionNet.getNeuron("output"));

		flatWorld.run();

		flatWorld.saveNN();

		Thread.sleep(1000);

		System.out.println("\n\n\n\n\n");

		System.out.println("Death Times: ");
		System.out.println(flatWorld.deathTime.toString());
		System.out.println("\n\n");

		System.out.println("Objects Eaten: " + flatWorld.objectsEaten);

		System.out.println("Weights:\n");
		System.out.println(flatWorld.weightInspector.getInspectionInfo());

		System.out.println("\n\n" + flatWorld.errorRecord.toString());

	}

}
